home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr33 / nbscom12.zip / NBSCOM.C next >
Text File  |  1993-05-05  |  17KB  |  495 lines

  1. /*-----------------------------------------------------------------------------
  2.   NBS ACTS Access Routine -- Dials NBS & sets DOS date/time accordingly.
  3.   Copyright (c) Franklin Antonio, 1988, All Rights Reserved.
  4.  
  5.   This program (source and object) may be freely distributed and used for non-
  6.   commercial purposes only.  If you redistribute this package, you must 
  7.   distribute all the files (source, object, doc, ini), in their original, 
  8.   unmodified, form.  You may, additionally, distribute modified versions, with
  9.   the modified versions, but they must be clearly identified as modified, with 
  10.   the original copyright statements intact, and the name and address of the 
  11.   modifier must be clearly shown.  
  12.  
  13.   Compile with Microsoft C 5.1 ... cl /Ox /DMAIN nbscom.c
  14.  
  15.   Edit History...
  16.   12/19/88 -fa- change abs to dabs in printf of time_change
  17.         added .ini file
  18.         put in direct i/o (to ignore modem control signals)
  19.         started hardware RTC stuff
  20.   12/20/88 -fa- replace sscanf() with atoi() in parse_a_time (AT:3.3ms-->0.3ms)
  21.         replace putc( ,stdio) with putc_screen()     (AT:1.5ms-->0.5ms)
  22.         check ftime() {1.5ms} and kbhit() {0.5ms} only every 10th char
  23.         now polled i/o won't drop chars on even the slowest machine
  24.   01/07/89 -fa- Timeout was too short for some people using pulse dialing or
  25.         using long access codes.  Changed default from 30 to 90 sec,
  26.         and made it a param settable from ini file.
  27.   ----------------------------------------------------------------------------*/
  28. #include <stdio.h>
  29. #include <bios.h>            /*needed by _bios_serialcom() */
  30. #include <dos.h>            /*needed by _dos_setxxxx() */
  31. #include <conio.h>            /*needed by kbhit() */
  32. #include <sys\timeb.h>            /*needed by ftime() */
  33. #include <time.h>            /*needed by tzset() */
  34. #include <ctype.h>            /*needed by isspace() */
  35. #include <graph.h>            /*needed by _clearscreen() */
  36. #include <math.h>            /*needed by fabs() */
  37.  
  38. /*function prototypes*/
  39. double floatime(struct timeb);
  40. double floatimenow(void);
  41. void delay(double);
  42. int parse_a_time(char *, int *, int *, int *, int *, int *, int *);
  43. void Datek2(long,long *,long *,long *);
  44. long KDAY2(long,long,long);
  45.  
  46.  
  47. #define dabs(x) fabs(x)                    /*double abs*/
  48. #define xbcd(bcd) ( 10*((bcd)>>4) + ((bcd)&0x0f) )  /*packed bcd to integer*/
  49. #define bcdx(x)   ( ((x)/10)<<4) + (x)%10 )        /*integer to packed bcd*/
  50.  
  51.  
  52. /*parameters initialized here, but set from NBSCOM.INI file if available*/
  53. int nbs_port = 0;                  /*port # for serial comm*/
  54. int nbs_speed = 1200;                  /*baud rate for serial comm*/
  55. int maxtime = 90;                  /*max online time (seconds*/
  56. char dial_sequence[50]   = "ATDT1-303-494-4774";  /*dial command buffer*/
  57. char hangup_sequence[50] = "ATH";          /*hangup command buffer*/
  58.  
  59.  
  60. /*items specific to standard 8250-based com ports*/
  61. unsigned port[4] ={0x3f8,0x2f8,0,0};        /*standard IBM port addresses*/
  62. #define UART_DATA 0                /*data register        */
  63. #define UART_MCR  4                /*modem control register*/
  64. #define UART_LSR  5                /*line status register */
  65. #define UART_MSR  6                /*modem status register*/
  66.  
  67.  
  68.  
  69. /*-----------------------------------------------------------------------------
  70.   main  (stand alone program)
  71.   ----------------------------------------------------------------------------*/
  72. #ifdef MAIN                    
  73. main() {                    /*compile main optionally*/
  74.   nbs_init_and_acts();
  75.   }
  76. #endif
  77.  
  78. nbs_init_and_acts() {
  79.   nbs_init();
  80.   printf("NBSCOM 1.2 -- Copyright(c) Franklin Antonio, 1988\n");
  81.   delay(1.0);
  82.   call_nbs_acts();
  83.   }
  84.  
  85. /*-----------------------------------------------------------------------------
  86.   call_nbs_acts -- Places a call to National Bureau of Standards  Advanced
  87.      Computer Time Service, reads date/time, & sets dos time accordingly.
  88.   ----------------------------------------------------------------------------*/
  89. call_nbs_acts() {
  90. struct timeb start,now,time_before,time_after;
  91. struct dosdate_t dosdate;
  92. struct dostime_t dostime;
  93. double time_change;
  94. long lda,lmo,lyr,time;
  95. int tick,j,j1,j2,yr1,yr2,mo1,mo2,da1,da2,hr1,hr2,mi1,mi2,se1,se2;
  96. int rtc,yr,mo,da,hr,mi,se;
  97. char *p,c,buf[80];
  98.  
  99.  
  100. j = _COM_CHR8 | _COM_NOPARITY;
  101. if(nbs_speed == 300)            /*user want low speed?*/
  102.   j |= _COM_300;            /*ok*/
  103. else
  104.   j |= _COM_1200;            /*only other speed NBS allows*/
  105. _bios_serialcom(_COM_INIT,nbs_port,j);    /*initialize modem port*/
  106.  
  107. _clearscreen(_GCLEARSCREEN);        /*clear screen, so no scroll during*/
  108.                     /*session.  Elims scn scroll time*/
  109.  
  110. printf("Accessing National Bureau of Standards \n"
  111.        "          Advanced Computer Time Service...\n\n");
  112.  
  113. printf("--- Modem dialog follows --- Type any key to abort. ---\n");
  114.  
  115. dial_the_modem();            /*initiate phonecall*/
  116.  
  117.  
  118. /*now sit in a loop for up to maxtime copying characters to screen, and a 
  119.   buffer.  when lf seen, attempt to parse.  two correct parses in a row which
  120.   differ by exactly 1 second causes successful termination.  Timeout or a hit
  121.   from the keyboard causes unsuccessful termination.  The most time-consuming
  122.   item in the loop (by far) is the ftime() call.  ftime() takes 6ms on a
  123.   4.77 MHz PC, which is 2/3 of a character time @ 1200 baud. ftime() is a
  124.   good example of a library routine that could have been 20 times faster
  125.   if it had been written carefully. */
  126.  
  127. fflush(stdin);                    /*flush kb, so kbhit works*/
  128. ftime(&start);                    /*begin timeout*/
  129. p=buf;                        /*init ptr to line buffer*/
  130.  
  131. for(tick=0; 1; tick = (tick>=10) ? 0 : ++tick) { /*serial port poll loop*/
  132.  
  133.   c = nbgetc_modem();                /*get a char from modem*/
  134.   if(c != 0) putc_screen(c);            /*echo to screen*/
  135.  
  136.   if(isprint(c) || isspace(c)) {        /*elim trash*/
  137.     *p++ = c;                    /*good char to buffer*/
  138.     if(c == '\n' || p == buf+80-1) {        /*eol?*/
  139.       *p=0; p=buf;                /*tie off & reset ptr*/
  140.       j = parse_a_time(buf,&yr1,&mo1,&da1,&hr1,&mi1,&se1);
  141.       if(j==1) {                /*a valid time line?*/
  142.         if(yr1==yr2 && mo1==mo2 && da1==da2 && hr1==hr2 && 
  143.            mi1==mi2 && se1==se2+1) {        /*and it's 2nd one?*/
  144.           goto goodtime;            /*zounds*/
  145.           }
  146.         else {
  147.           yr2=yr1; mo2=mo1; da2=da1;        /*then it's 1st one*/
  148.           hr2=hr1; mi2=mi1; se2=se1;        /*remember values*/
  149.           }
  150.         }    /*end if j==1 */
  151.       }      /*end if c == */
  152.     }        /*end if isprint( */
  153.  
  154.   if(tick == 0) {                /*occasionally check*/
  155.     ftime(&now);                /*for timeout*/
  156.     if(now.time > start.time + maxtime) break;
  157.     }
  158.   if(tick == 5) {                /*occasionally check*/
  159.     if(kbhit()) break;                /*for manual abort*/
  160.     }
  161.   }          /*end for(tick   */
  162.  
  163.  
  164. if(kbhit()) printf("--- aborted by user ---\n");
  165. else        printf("--- %d seconds elapsed.  aborted. ---\n",maxtime);
  166. time_change = 0.;                /*there was no change*/
  167.  
  168. goto hangup;                    /*attempt to hang up phone*/
  169.  
  170.  
  171.  
  172. /* ------- Here when we have read a good time.  Tell DOS. ---------*/
  173.  
  174. /*we have to adjust the UTC time we've just received by our local timezone
  175.   so we can set DOS date/time local.  Adjustment may be negative, and the lib.
  176.   routine mktime() can't handle negative arguments, so we convert using local
  177.   routines to a single time variable, adjust, then convert back.  messy.*/
  178.  
  179. goodtime:                    /*got a valid time*/
  180. tzset();                    /*make timezone valid*/
  181. time = 24L*KDAY2((long)da1,(long)mo1,(long)1900+yr1) + hr1;
  182. time -= timezone/(60L*60L);            /*adjust for timezone*/
  183. Datek2(time/24L,&lda,&lmo,&lyr);        /*get new d/m/y*/
  184. hr1 = time%24L;                    /*fractional day*/
  185.  
  186. /*Now we have local time.  Do all the time-setting operations before printing
  187.   any of the results, or doing any of the calls that use floating-point 
  188.   operations, so that we get everything set before much time passes.   */
  189.  
  190. ftime(&time_before);                /*time before setting*/
  191.  
  192. dostime.hour   = hr1;
  193. dostime.minute = mi1;
  194. dostime.second = se1;
  195. dostime.hsecond = 0;
  196. j2 = _dos_settime(&dostime);            /*set dos time*/
  197.  
  198. dosdate.year  = lyr;
  199. dosdate.month = lmo;
  200. dosdate.day   = lda;
  201. dosdate.dayofweek = 0;
  202. j1 = _dos_setdate(&dosdate);            /*set dos date*/
  203.  
  204. ftime(&time_after);                /*time after setting*/
  205.  
  206. rtc = read_hw_rtc(&yr,&mo,&da,&hr,&mi,&se);    /*hardware realtime clock*/
  207.  
  208.  
  209. printf("--- Setting DOS Date & Time to local: %02d/%02d/%d %02d:%02d:%02d ---\n",
  210.        dosdate.month,dosdate.day,dosdate.year,
  211.        dostime.hour,dostime.minute,dostime.second);
  212.  
  213. if(j1 != 0) printf("--- DOS set Date failed ---\n");
  214. if(j2 != 0) printf("--- DOS set Time failed ---\n");
  215. if(j1 == 0 && j2 == 0) {
  216.   time_change = floatime(time_after) - floatime(time_before);    /*change*/
  217.   printf("--- Your DOS time was %s NBS by %.2f seconds ---\n",
  218.          time_change>0 ? "behind" : "ahead of", 
  219.          dabs(time_change)  );
  220.   }
  221. if(rtc) printf("--- Info only: Your hardware realtime clock reads: "
  222.                "%02d/%02d/%d %02d:%02d:%02d ---\n",
  223.                mo,da,yr,hr,mi,se);
  224.  
  225. goto hangup;                    /*attempt to hang up phone*/
  226.  
  227.  
  228. hangup:
  229. hangup_the_modem();                /*terminate phone call*/
  230.  
  231. printf("--- Done --- Phone call duration was %.1f seconds. ---\n",
  232.        floatimenow() - floatime(start) - time_change );
  233.  
  234. return 0;
  235.  
  236. }
  237.  
  238.  
  239. /*-----------------------------------------------------------------------------
  240.   dial_the_modem -- does just that.  initiates phone call.  
  241.   ----------------------------------------------------------------------------*/
  242. dial_the_modem() {
  243.  
  244. control_modem(0x3);            /*turn on DTR & RTS*/
  245.  
  246. printf    ("+++");
  247. puts_modem("+++");            /*enter hayes command mode*/
  248. delay(1.25);                /* +++ guardtime*/
  249.  
  250. printf(    dial_sequence);        /*dial!*/
  251. puts_modem(dial_sequence);
  252.  
  253. printf("\n");                /*dial command terminator*/
  254. puts_modem("\r\n");
  255. delay(0.1);                /*avoid echo last char of dial*/
  256. }
  257.  
  258.  
  259.  
  260. /*-----------------------------------------------------------------------------
  261.   hangup_the_modem -- does just that.  terminates phone call.  
  262.   ----------------------------------------------------------------------------*/
  263. hangup_the_modem() {
  264.  
  265. printf    ("+++");
  266. puts_modem("+++");                /*enter hayes command mode*/
  267. delay(1.25);                    /* +++ guardtime*/
  268.  
  269. printf(    hangup_sequence);            /*hangup command*/
  270. puts_modem(hangup_sequence);
  271.  
  272. printf("\n");
  273. puts_modem("\r\n");
  274.  
  275. control_modem(0x00);            /*drop DTR (hangs up some modems) */
  276. }
  277.  
  278.  
  279.  
  280. /*-----------------------------------------------------------------------------
  281.   parse_a_time -- attempt to parse a "time" line from NBS
  282.                     MJD  YR MO DA  H  M  S ST S UT1 msADV         OTM
  283.   nbs format-->    47511 88-12-16 06:03:44 00 0 -.1 045.0 UTC(NBS) *
  284.   @ 1200 baud      47511 88-12-16 06:03:45 00 0 -.1 045.0 UTC(NBS) *
  285.                     H  M  S msADV OTM
  286.   nbs format-->    06:03:44 045.0  *   <--but how do i get the date?
  287.   @ 300 baud       06:03:45 045.0  *
  288.   ----------------------------------------------------------------------------*/
  289.  
  290. parse_a_time(char *p, int *yr, int *mo, int *da,
  291.              int *hr, int *mi, int *se) {
  292. int mjd;
  293.  
  294. /*This scanf takes 3.3ms on my AT, but the following code that replaces it 
  295.   takes 0.3ms, so is a clear winner.
  296.   j = sscanf(line,"%d %d-%d-%d %d:%d:%d",&mjd,yr,mo,da,hr,mi,se);
  297. */
  298. while(isspace(*p)) p++;        
  299. while(isdigit(*p)) p++;            /*past mjd*/
  300. while(isspace(*p)) p++;
  301. *yr = atoi(p); p+=3;            /*date*/
  302. *mo = atoi(p); p+=3; 
  303. *da = atoi(p); p+=3; 
  304. while(isspace(*p)) p++;
  305. *hr = atoi(p); p+=3;            /*time*/
  306. *mi = atoi(p); p+=3;
  307. *se = atoi(p); p+=3;
  308.  
  309. if(*yr<88 || *yr>99 || *mo<=0 || *mo>12 || *da<=0 || *da>31 || 
  310.    *hr<0  || *hr>23 || *mi<0  || *mi>59 || *se<0  || *se>59 ) return 0;
  311.                     /*and legal values everywhere?*/
  312.  
  313. return 1;                /*good, then we got it*/
  314. }
  315.  
  316.  
  317.  
  318. /*-----------------------------------------------------------------------------
  319.   delay -- spinloop delay for wait seconds.  
  320.   ----------------------------------------------------------------------------*/
  321. void delay(double wait) {
  322. struct timeb start;
  323. ftime(&start);
  324. while(floatimenow() < floatime(start) + wait);
  325. }
  326.  
  327.  
  328. /*-----------------------------------------------------------------------------
  329.   floatime -- converts a timeb structure to floating point seconds.  Used 
  330.               outside critical time sections.
  331.   ----------------------------------------------------------------------------*/
  332. double floatime(struct timeb t) {
  333. return t.time + .001*t.millitm;
  334. }
  335.  
  336. double floatimenow() {
  337. struct timeb t;
  338. ftime(&t);
  339. return floatime(t);
  340. }
  341.  
  342.  
  343.  
  344. /*-----------------------------------------------------------------------------
  345.   nonblocking read character from serial port using direct i/o
  346.   ----------------------------------------------------------------------------*/
  347. nbgetc_modem() {
  348. if((inp(port[nbs_port]+UART_LSR) & 0x01) == 0)    /*character ready?*/
  349.   return 0;                    /*no*/
  350. return inp(port[nbs_port]+UART_DATA);        /*yes*/
  351. }
  352.  
  353.  
  354. /*-----------------------------------------------------------------------------
  355.   write character to serial port using direct i/o
  356.   ----------------------------------------------------------------------------*/
  357. putc_modem(char c) {
  358. while((inp(port[nbs_port]+UART_LSR) & 0x20) == 0); /*wait for holding reg*/
  359. outp(port[nbs_port]+UART_DATA, c);
  360. }
  361.  
  362.  
  363. /*-----------------------------------------------------------------------------
  364.   write string to serial port using direct i/o
  365.   ----------------------------------------------------------------------------*/
  366. puts_modem(char *s) {
  367. while(*s != 0) 
  368.   putc_modem(*s++);
  369. }
  370.  
  371.  
  372. /*-----------------------------------------------------------------------------
  373.   set modem-control signals on serial port using direct i/o
  374.   ----------------------------------------------------------------------------*/
  375. control_modem(char c) {
  376. outp(port[nbs_port]+UART_MCR,c);
  377. }
  378.  
  379.  
  380.  
  381. /*-----------------------------------------------------------------------------
  382.   put character to screen using bios.  putc(c,stdio) took 1.4 mS, this takes
  383.   0.5 mS on my AT.
  384.   ----------------------------------------------------------------------------*/
  385. putc_screen(char c) {
  386. static union REGS reg;
  387.  
  388. reg.h.ah = 0xE;                /*write tty style*/
  389. reg.h.al = c;                /*this char*/
  390. reg.h.bh = 0;                /*scn page 0*/
  391. int86(0x10,®,®);            /*do it*/
  392. }
  393.  
  394.  
  395.  
  396. /*----------------------------------------------------------------------------
  397.   ACM Algorithm 199
  398.   convert calendar date to day number
  399.   K=1 at March 1, 1900.
  400.   --------------------------------------------------------------------------*/
  401. long KDAY2(long Iday,long Month,long Iyear) {
  402. long M,IY,Kday;
  403. if(Iday < 1 || Iday > 31 || Month < 1 || Month > 12
  404.    || Iyear < 1900 || Iyear > 1999) printf("?kday: ilg param\n");
  405.  
  406. M=Month-3 ; IY=Iyear-1900;
  407. if(Month <= 2) {
  408.   M=Month+9;
  409.   IY=IY-1;
  410.   }
  411. Kday=(1461*IY)/4+(153*M+2)/5+Iday;
  412. return Kday;
  413. }
  414.  
  415.  
  416.  
  417. /*----------------------------------------------------------------------------
  418.   ACM algorithm 199
  419.   day number to calendar date
  420.   Valid from 3/1/1900 thru 2/28/2000.
  421.   --------------------------------------------------------------------------*/
  422. void Datek2(long K,long *Iday,long *Month,long *Iyear) {
  423. long day,month,year;
  424.  
  425. if(K < 0 || K > 36500) printf("?datek: ilg param\n");
  426. year=(4*K-1)/1461;
  427. day=4*K-1-1461*year;
  428. day=(day+4)/4;
  429. month=(5*day-3)/153;
  430. day=5*day-3-153*month;
  431. day=(day+5)/5;
  432. month=month+3;
  433. year=year+1900;
  434. if(month >= 13) {
  435.   month=month-12;
  436.   year=year+1;
  437.   }
  438. *Iday=day; *Month=month; *Iyear=year;
  439. }
  440.  
  441.  
  442.  
  443. /*----------------------------------------------------------------------------
  444.   Parameter initialization (reads .INI file)
  445.   --------------------------------------------------------------------------*/
  446.  
  447. nbs_init() {
  448. FILE *init;
  449. char buf[80],*p;
  450.  
  451. init = fopen("nbscom.ini","r");            /*open .ini file*/
  452. if(init == NULL) return 0;            /*if no file, use defaults*/
  453.  
  454. while(NULL != fgets(buf,80,init)) {        /*until end-of-file*/
  455.   sscanf(buf," dial = %50s",  dial_sequence);
  456.   sscanf(buf," hangup = %50s",hangup_sequence);
  457.   sscanf(buf," port = %d",    &nbs_port);
  458.   sscanf(buf," speed = %d",   &nbs_speed);
  459.   sscanf(buf," maxtime = %d",  &maxtime);
  460.   }
  461.  
  462. fclose(init);                    /*close .ini file*/
  463. }
  464.  
  465.  
  466. /*-----------------------------------------------------------------------------
  467.   Routines to diddle the hardware realtime-clock (via BIOS)
  468.   It's difficult to set the RTC accurately, because its least digit is seconds.
  469.   Furthermore, the RTC can refuse to be read (if it's propagating a carry at
  470.   the time).  I plan to write some code that overcomes these difficulties, so
  471.   that i can then set the RTC accurately.  
  472.   This only works on an IBMAT or later BIOS.  It would also work if aftermarket
  473.   RTCs came with TSRs that implemented the AT BIOS RTC functions, but none do.
  474.   **NOTFINISHED**
  475.   ----------------------------------------------------------------------------*/
  476. read_hw_rtc(int *yr,int *mo,int *da,int *hr,int *mi,int *se) {
  477. union REGS reg;
  478.  
  479. reg.h.ah = 4;                    /*read date from RTC*/
  480. reg.h.cl = reg.h.ch = 0xff;            /*marker*/
  481. int86(0x1A,®,®);                /*BIOS call*/
  482. if(reg.h.cl==0xff || reg.h.ch==0xff) return 0;    /*if no RTC BIOS, fail return*/
  483. *yr = 100*xbcd(reg.h.ch) + xbcd(reg.h.cl);
  484. *mo = xbcd(reg.h.dh);
  485. *da = xbcd(reg.h.dl);
  486.  
  487. reg.h.ah = 2;                    /*read time from RTC*/
  488. int86(0x1A,®,®);                /*BIOS call*/
  489. *hr = xbcd(reg.h.ch);
  490. *mi = xbcd(reg.h.cl);
  491. *se = xbcd(reg.h.dh);
  492. }
  493.  
  494.  
  495.